package vn.edu.hut.soict.translate.transform;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import vn.edu.hut.soict.alignment.BiDirectionalTokenAlignment;
import vn.edu.hut.soict.lienkate.dictionary.Dictionary;
import vn.edu.hut.soict.lienkate.dictionary.DictionaryReader;
import vn.edu.hut.soict.lienkate.dictionary.VietDictionaryReader;
import vn.edu.hut.soict.lienkate.parser.Linkage;
import vn.edu.hut.soict.lienkate.parser.NewLinkageDrawer;
import vn.edu.hut.soict.lienkate.parser.exprprune.ExpressionPruneParser;
import vn.edu.hut.soict.translate.database.TranslatorDatabase;
import vn.edu.hut.soict.translate.dictionary.bilingual.BilingualDictionaryWithPOS;
import vn.edu.hut.soict.translate.dictionary.bilingual.BilingualDictionaryWithPOSReader;
import vn.edu.hut.soict.translate.dictionary.bilingual.PartOfSpeech;
import vn.edu.hut.soict.translate.search.Operator;
import vn.edu.hut.soict.translate.search.Record;
import vn.edu.hut.soict.translate.wordgraph.Word;

public class InnerTransformer {
	private static BilingualDictionaryWithPOS bilingualDictionary;
	private static Dictionary enDictionary;
	private static Dictionary viDictionary;

	private ExpressionPruneParser enParser;
	private ExpressionPruneParser viParser;
	private TranslatorDatabase database;

	private List<Record> source;
	List<String> enTokenList;
	List<String> viTokenList;
	List<String> inputTokenList;
	private String enSentence;
	private String viSentence;
	Linkage enLinkage;
	Linkage viLinkage;
	Linkage inputLinkage;

	BiDirectionalTokenAlignment alignment;

	/*
	 * Map a Vietnamese token to its corresponding records.
	 */
	private Map<Integer, Record> tokenToRecordMap;

	/*
	 * Map an English phrase (a.k.a. a group of tokens) to their corresponding Vietnamese phrase.
	 */
	private Map<List<Integer>, List<Integer>> phraseMap;

	/*
	 * Map an input token to a source token.
	 */
	Map<Integer, Integer> inputToSourceMap;

	private SubstitutionTransformer sTransformer;
	private DeletionTransformer dTransformer;
	private InsertionTransformer iTransformer;

	static {
		DictionaryReader enReader = new DictionaryReader();
		VietDictionaryReader viReader = new VietDictionaryReader();
		try {
			enDictionary = enReader.read("res/dictionary/lg-en/4.0.dict");
			viDictionary = viReader.read("res/dictionary/lg-vi-new/dictionary.txt");
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		catch (ParseException e) {
			e.printStackTrace();
		}

		BilingualDictionaryWithPOSReader reader = new BilingualDictionaryWithPOSReader();
		bilingualDictionary = reader.read(BilingualDictionaryWithPOS.DICTIONARY_FILE);
	}

	public InnerTransformer() {
		enParser = new ExpressionPruneParser(enDictionary);
		viParser = new ExpressionPruneParser(viDictionary);
		database = new TranslatorDatabase();
		database.connect();
	}

	public String getVietnameseSentence() {
		return viSentence;
	}

	public String getEnglishSentence() {
		return enSentence;
	}

	private void init(List<Record> source) {
		this.source = source;
		initSentences();
		initLinkages();
		initMapping();
		initTransformer();
	}

	private void initSentences() {
		StringBuilder sb = new StringBuilder();
		viTokenList = new ArrayList<String>();
		inputTokenList = new ArrayList<String>();
		for (Record record : source) {
			Word graphWord = record.getGraphWord();
			Word inputWord = record.getInputWord();
			if (graphWord != null) {
				String text = graphWord.getText();
				sb.append(text).append(" ");
				viTokenList.add(text);
			}
			if (inputWord != null) {
				inputTokenList.add(inputWord.getText());
			}
		}
		viSentence = sb.toString().trim();
		System.out.println("Câu ví dụ: " + viSentence);
		System.out.println("Dãy biến đổi: " + source);
		enSentence = database.getEnglishSentence(viSentence);
		alignment = new BiDirectionalTokenAlignment(viSentence, enSentence, database.getAlignment(viSentence));
		enTokenList = alignment.getDestination();
	}

	private void initMapping() {
		tokenToRecordMap = new HashMap<Integer, Record>();
		int index = 0;
		for (Record record : source) {
			if (record.getOperator() != Operator.INSERTION) {
				tokenToRecordMap.put(index++, record);
			}
		}

		inputToSourceMap = new HashMap<Integer, Integer>();
		int iSource = 0, iInput = 0;
		for (Record record : source) {
			switch (record.getOperator()) {
				case EXACT_MATCH:
				case SUBSTITUTION:
					inputToSourceMap.put(iInput++, iSource++);
					break;
				case INSERTION:
					iInput++;
					break;
				case DELETION:
					iSource++;
					break;
			}
		}

		phraseMap = new HashMap<List<Integer>, List<Integer>>();
		for (int i = 0; i < enTokenList.size(); i++) {
			List<Integer> values = alignment.sourceIndexesAlignWith(i);
			Iterator<List<Integer>> iterator = phraseMap.keySet().iterator();
			if (phraseMap.containsValue(values)) {
				while (iterator.hasNext()) {
					List<Integer> key = iterator.next();
					if (values == null ? phraseMap.get(key) == null
							: phraseMap.get(key).equals(values)) {
						iterator.remove();
						key.add(i);
						phraseMap.put(key, values);
						break;
					}
				}
			}
			else {
				List<Integer> key = new ArrayList<Integer>();
				key.add(i);
				phraseMap.put(key, values);
			}
		}
	}

	private void initLinkages() {
		List<Linkage> enLinkages = null;
		List<Linkage> viLinkages = null;
		List<Linkage> inputLinkages = null;
		enLinkage = null;
		viLinkage = null;
		inputLinkage = null;
		try {
			enLinkages = enParser.parse(enTokenList, 0);
			System.out.println(NewLinkageDrawer.toString(enLinkages, enTokenList));
		}
		catch (ParseException e) {
			System.err.println(e.getMessage());
		}

		try {
			viLinkages = viParser.parse(viTokenList, 0);
			System.out.println(NewLinkageDrawer.toString(viLinkages, viTokenList));
		}
		catch (ParseException e) {
			System.err.println(e.getMessage());
		}

		try {
			inputLinkages = viParser.parse(inputTokenList, 0);
			System.out.println(NewLinkageDrawer.toString(inputLinkages, inputTokenList));
		}
		catch (ParseException e) {
			System.err.println(e.getMessage());
		}

		enLinkage = enLinkages != null && enLinkages.size() > 0
				? enLinkages.get(0) : null;
		viLinkage = viLinkages != null && viLinkages.size() > 0
				? viLinkages.get(0) : null;
		inputLinkage = inputLinkages != null && inputLinkages.size() > 0
				? inputLinkages.get(0) : null;
	}

	private void initTransformer() {
		sTransformer = new SubstitutionTransformer(this);
		dTransformer = new DeletionTransformer(this);
		iTransformer = new InsertionTransformer(this);
	}

	public String transform(List<Record> source) {
		if (source == null) {
			return null;
		}

		init(source);

		if (enSentence == null) {
			System.err.println("Database error: Cannot find corresponding english sentence.");
			return null;
		}

		System.out.print("Substitution: " + resultToString() + " -> ");
		transformSubsitution();
		System.out.println(resultToString());

		System.out.print("Deletion: " + resultToString() + " -> ");
		transformDeletion();
		System.out.println(resultToString());

		System.out.print("Insertion: " + resultToString() + " -> ");
		transformInsertion();
		System.out.println(resultToString());

		return resultToString();
	}

	private String chooseBestMeaning(List<String> meanings) {
		if (meanings != null && meanings.size() > 0) {
			return meanings.get(0);
		}
		return null;
	}

	private void transformSubsitution() {
		for (Entry<List<Integer>, List<Integer>> entry : phraseMap.entrySet()) {
			List<Integer> key = entry.getKey();
			List<Integer> value = entry.getValue();

			if (key.size() == 0) {
				continue;
			}

			int index = key.get(0);

			for (Integer i : value) {
				if (i != null) {
					Record record = tokenToRecordMap.get(i);
					if (record.getOperator() == Operator.SUBSTITUTION) {
						transformSubsitution(index, record);
						if (key.size() > 1) {
							for (int j = 1; j < key.size(); j++) {
								enTokenList.set(key.get(j), "");
							}
						}
					}
				}
			}

		}
	}

	private void transformSubsitution(int i, Record record) {
		String viWord = record.getInputWord().getText();
		PartOfSpeech pos = PartOfSpeech.valueOf(record.getInputWord().partOfSpeech());

		List<String> meanings = bilingualDictionary.meaning(viWord, pos);
		String bestMeaning = viWord;
		if (meanings != null) {
			bestMeaning = chooseBestMeaning(meanings);
		}

		sTransformer.substitute(i, bestMeaning);
	}

	private void transformDeletion() {
		for (int i = 0; i < viTokenList.size(); i++) {
			Record record = tokenToRecordMap.get(i);
			if (record.getOperator() == Operator.DELETION) {
				dTransformer.delete(i);
			}
		}
	}

	private void transformInsertion() {
		int index = 0;
		for (Record record : source) {
			if (record.getOperator() == Operator.INSERTION) {
				String viWord = record.getInputWord().getText();
				PartOfSpeech pos = PartOfSpeech.valueOf(record.getInputWord().partOfSpeech());
				List<String> meanings = bilingualDictionary.meaning(viWord, pos);
				String bestMeaning = viWord;
				if (meanings != null) {
					bestMeaning = chooseBestMeaning(meanings);
				}
				iTransformer.insert(index++, bestMeaning);
			}
			else if (record.getOperator() != Operator.DELETION) {
				index++;
			}
		}
	}

	private String resultToString() {
		StringBuilder sb = new StringBuilder();
		for (String s : enTokenList) {
			if (s != null) {
				sb.append(s).append(" ");
			}
		}
		if (sb.length() > 0) {
			sb.deleteCharAt(sb.length() - 1);
		}

		return sb.toString();
	}

}
